#ifndef __MY_UTIL__
#define __MY_UTIL__ 
#include <iostream>
#include <vector>
#include <time.h>
#include <fstream>
#include <sstream>
#include <string>
#include "bundle.h"
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>
#include <sys/stat.h>

using namespace std;

namespace fs = std::experimental::filesystem;
//c++17 中的文件系统库
namespace cloud
{
  //自己的命名空间叫cloud，防止与外界命名冲突
  class FileUtil{
    //文件工具类
    private:
      std::string _name;//文件名
    public:
      //构造函数
      FileUtil(const std::string &name): _name(name){}
      std::string Name()
      {
        return fs::path(_name).filename().string();//path(_name)实例化_name,通过filename只获取文件名，返回去除路径后的纯文件名
      }
      //几个属性获取接口
      //文件是否存在
      bool Exists()
      {
        return fs::exists(_name);//传文件名
      }
      //文件大小的获取
      size_t Size()
      {
        if(this->Exists() == false)//如果文件不存在，则不需要进行文件大小的获取
        {
          return 0;
        }
        return fs::file_size(_name);
      }
      //文件最后一次修改时间
      time_t MTime()
      {
        if(this->Exists() == false)//如果文件不存在，则无法进行对应查询
        {
          return 0;
        }
        auto ftime = fs::last_write_time(_name);//此时获取的不是整型时间，所以需要进行转换
        std::time_t cftime = decltype(ftime)::clock::to_time_t(ftime);
        return cftime;//获取到想要的时间戳
      }
      //文件最后一次访问时间
      time_t ATime()
      {
        if(this->Exists() == false)//如果文件不存在，则无法进行对应查询
        {
          return 0;
        }
        struct stat st;//系统调用接口
        stat(_name.c_str(), &st);//c_str()是为了把string类对象转换成和c兼容的char*类型，
        //&st获取它的属性
        return st.st_atime;
      }
      //读取文件所有数据 body用来接收文件数据
      bool Read(std::string *body)
      {
        if (this->Exists() == false)
        {
          return false;
        }
        std::ifstream ifs;//定义ifs，ifstream是一个输入流    
        ifs.open(_name, std::ios::binary);//以二进制方式打开文件name    
        if(ifs.is_open() == false)   
        {    
          std::cout << "read open failed!\n";    
          return false;    
        }    
        size_t fsize = this->Size();//调用获取文件大小的接口即可
        body->resize(fsize);//body指向resize，重新开辟指定大小为fsize的空间    
        ifs.read(&(*body)[0],fsize);//对于body空间进行解引用再对第0个元素取地址，    
        //从而访问到了body中的首地址    
        if(ifs.good() == false)//判断是否读取成功，如果上一步操作失败了，则读取失败    
        {    
          std::cout << "read file failed!\n";    
          ifs.close();//关闭文件    
          return false;    
        }
        //如果读取成功，则也关闭文件，文件内容读取到了body中去
        ifs.close();
        return true;

      }
      //将body中的数据写入文件
      bool Write(const std::string &body)
      {
        std::ofstream ofs;//定义ofs，ofstream是一个输出流    
        ofs.open(_name, std::ios::binary);//以二进制方式打开文件name    
        if(ofs.is_open()== false)    
        {    
          std::cout << "write open failed!\n";    
          return false;    
        }    
        ofs.write(body.c_str(), body.size());//第一个参数代表把body中的数据写入，                           
        //第二个参数代表要写入的长度为body的size大小    
        if(ofs.good() == false)//判断是否写入成功，如果上一步操作失败了，则写入失败    
        {    
          std::cout << "write file failed!\n";    
          ofs.close();//关闭文件    
          return false;    
        }    
        ofs.close();//写入成功了，也关闭文件 
        return true;
      }
      //创建目录
      bool CreateDirectory()
      {
        //如果存在则不需要创建
        if(this->Exists())
        {
          return true;
        }
        fs::create_directories(_name);//使用文件系统库中的对应接口
        return true;
      }
      //遍历目录，获取目录下所有文件的路径名
      bool ScanDirectory(std::vector<std::string> *arry)
      {
        if(this->Exists() == false)//文件名若不存在则不需要遍历
        {
          return false;
        }
        //目录迭代器当前默认只能遍历深度为一层的目录
        for(auto &a : fs::directory_iterator(_name))//调用库中的目录迭代器
        {
          if(fs::is_directory(a) == true)//is_directory()是文件库中判断是否是文件夹的接口
          {
            continue;//如果当前文件是一个文件夹，则不处理，遍历下一个
          }
          //当前我们的目录遍历中，只获取普通文件信息，针对目录不做深度处理
          //fs::path(a) 实例化了一个文件系统库中专门针对文件处理的一个path类对象
          // fs::path::filename()成员接口是获取这个文件的文件名(不含路径),注意不是string
          // 所以后面使用string成员接口转换成string对象返回
          //std::string pathname = fs::path(a).filename().string();//纯文件名
          //
          //relative_path()这个接口是带有文件给与路径的文件名接口
          std::string pathname = fs::path(a).relative_path().string();//带有路径的文件名
          arry->push_back(pathname);//在arry里面添加文件名

        }
        return true;
      }

      bool Remove()
      {
        if(this->Exists() == false)//如果文件不存在则不需要删除
        {
          return true;
        }
        fs::remove_all(_name);//删除源文件
        return true;
      }
      //文件压缩，针对普通文件进行压缩
      bool Compress(const std::string &packname)//packname为压缩包名称，将文件数据进行压缩存储到压缩包中
      {

        if(this->Exists() == false)//文件名若不存在则不需要压缩
        {
          return false;
        }
        std::string body;
        if(this->Read(&body) == false)//压缩读取文件数据失败
        {
          std::cout << "compress read file failed\n";
          return false;
        }
        std::string packed = bundle::pack(bundle::LZIP, body);//packed为压缩后的数据
        //将压缩后的数据packed写入到packname压缩包中
        if(FileUtil(packname).Write(packed) == false)
        {
          std::cout << "compress write pack data failed\n";//压缩向压缩包写入数据过程失败
        }
        fs::remove_all(_name);//压缩完成后将源文件移除
        return true;
      }
      //文件解压缩，针对压缩包进行解压缩
      bool UnCompress(const std::string &filename)//filename为原文件名称
      {

        if(this->Exists() == false)//文件名若不存在则不需要解压缩
        {
          return false;
        }
        std::string body;
        if(this->Read(&body) == false)
        {
          std::cout << "uncompress read pack data failed\n";//解压缩读取压缩包数据失败
          return false;
        }
        std::string unpack_data = bundle::unpack(body);//对body进行解压缩,将解压缩后数据unpack_data返回
        //将解压缩后的数据unpack_data写入到原文件filename中
        if(FileUtil(filename).Write(unpack_data) == false)
        {
          std::cout << "uncompress write file data failed\n";
          return false;
        }
        fs::remove_all(_name);//完成解压缩后删除压缩包
        return true;
      }
  };


  class JsonUtil{
    //Json工具类
    public:
      //静态成员函数，没有this指针，外部可以直接通过JsonUtil::Serialize()直接进行调用，而不需要       实例化对象
      //将Json::Value对象中的各种数据序列化成一个Json格式的字符串，写到body中
      static bool Serialize(Json::Value &val, std::string *body)
      {
        Json::StreamWriterBuilder swb;
        //StreamWriter类无法直接使用，需要使用StreamBuilder类进行new操作
        Json::StreamWriter *sw = swb.newStreamWriter();//new一个StreamWriter
        std::stringstream ss;//字符串流，用来存放需要被cout的字符串，是一个cout对象
        int ret = sw->write(val,&ss);//实现序列化
        if(ret != 0)//查看write接口后可知，成功返回0，那么若不为0，则序列化失败
        {
          std::cout << "Serialize failed!\n";
          delete sw;
          return false;
        }
        *body = ss.str();//body进行赋值
        delete sw;//释放sw，避免内存泄漏
        return true;
      }
      //将一个Json字符串进行反序列化，得到一个Json::Value *val 
      static bool UnSerialize(const std::string &body, Json::Value *val)
      {
        Json::CharReaderBuilder crb;//CharReader类无法直接使用，需要使用CharBuilder类进行new操作
        Json::CharReader *cr = crb.newCharReader();
        std::string err;//用来获取错误信息
        bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), val ,&err );
        //第一个参数为字符串起始地址，第二个参数为字符串结束地址
        //第三个参数为Json::Value对象指针，第四个参数为错误信息获取
        if(ret==false)
        {
          std::cout << "UnSerialize failed" << err << std::endl;
          delete cr;
          return false;
        }
        delete cr;
        return true;
      }


  };
}

#endif 
